﻿namespace Sai.Core.Introspection
{
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;

	public class EnumValue : ISourceCodeProvider, IEquatable<EnumValue>
	{
		public EnumValue()
		{
		}

		public EnumValue(string name, long value)
		{
			this.Name = name;
			this.Value = value;
		}

		public string Name { get; set; }

		public long Value { get; set; }

		public override string ToString()
		{
			return string.Format("{0} ({1})", this.Name, this.Value);
		}

        public override int GetHashCode()
        {
            return this.Name.GetHashCode() ^ this.Value.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            EnumValue ev = obj as EnumValue;

            if ((object)ev == null)
            {
                return false;
            }

            return this.Name == ev.Name && this.Value == ev.Value;
        }

        public bool Equals(EnumValue other)
        {
            if ((object)other == null)
            {
                return false;
            }

            return this.Name == other.Name && this.Value == other.Value;
        }

        public static bool operator ==(EnumValue a, EnumValue b)
        {
            if (object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            return a.Name == b.Name && a.Value == b.Value;
        }

        public static bool operator !=(EnumValue a, EnumValue b)
        {
            return !(a == b);
        }

        public SourceCode ToCode()
        {
            SourceCode code = new SourceCode();

            code.Add(new IdentifierTag(this.Name));
            code.Add(new PunctuationTag(string.Format (" = {0}", this.Value)));

            return code;
        }
    }

	public class EnumTypeDeclaration : ITypeDeclaration, IEquatable<ITypeDeclaration>, ISourceCodeProvider
	{
		private ObsoleteMarker obsolete;
		private List<EnumValue> allowedValues = new List<EnumValue>();

		public EnumTypeDeclaration()
		{
		}

		public EnumTypeDeclaration(TypeName typeName) 
		{
			this.TypeName = typeName;
		}

		public EnumTypeDeclaration(TypeName typeName, ObsoleteMarker marker)
		{
			this.TypeName = typeName;
			this.obsolete = new ObsoleteMarker
			{
				IsEnabled = marker.IsEnabled,
				Reason = marker.Reason
			};
		}

		public TypeName TypeName { get; set; }

		public TypeTaxonomy Taxonomy
		{
			get
			{
				return TypeTaxonomy.Enum;
			}
		}

		public bool IsObsolete
		{
			get
			{
				if (this.obsolete == null)
				{
					return false;
				}

				return this.obsolete.IsEnabled;
			}
		}

		public string ObsoleteReason
		{
			get
			{
				if (this.obsolete != null
					&& !string.IsNullOrEmpty(this.obsolete.Reason))
				{
					return this.obsolete.Reason;
				}

				return string.Empty;
			}
		}

		public ReadOnlyCollection<EnumValue> AllowedValues
		{
			get
			{
				return new ReadOnlyCollection<EnumValue>(this.allowedValues);
			}
		}

		public static bool operator ==(EnumTypeDeclaration a, EnumTypeDeclaration b)
		{
			if (object.ReferenceEquals(a, b))
			{
				return true;
			}

			if (((object)a == null) || ((object)b == null))
			{
				return false;
			}

			return a.TypeName == b.TypeName;
		}

		public static bool operator !=(EnumTypeDeclaration a, EnumTypeDeclaration b)
		{
			return !(a == b);
		}

		public void Add(string name, long value)
		{
			this.allowedValues.Add(new EnumValue(name, value));
		}

		public override string ToString()
		{
			return this.TypeName.ToString();
		}

		public override int GetHashCode()
		{
			return this.TypeName.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}

			EnumTypeDeclaration et = obj as EnumTypeDeclaration;

			if ((object)et == null)
			{
				return false;
			}

			return this.TypeName == et.TypeName;
		}

		public bool Equals(ITypeDeclaration other)
		{
			if ((object)other == null)
			{
				return false;
			}

			return this.TypeName.FullName == other.TypeName.FullName;
		}

		public bool MatchesType(ITypeDeclaration other)
		{
			EnumTypeDeclaration otherEnum = other as EnumTypeDeclaration;

			return otherEnum != null;
		}

        public SourceCode ToCode()
        {
            SourceCode code = new SourceCode();

            code.Add(new IdentifierTag(this.TypeName.Name));

            return code;
        }
    }
}
